﻿using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;

namespace Anno.DynamicApi
{
    public static class RuntimeCodeCompiler
    {
        private static volatile Dictionary<string, Assembly> cache = new Dictionary<string, Assembly>();
        private static object syncRoot = new object();
        static Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>();
        public static List<string> OtherAssemblys = new List<string>();
        static RuntimeCodeCompiler()
        {
            AppDomain.CurrentDomain.AssemblyLoad += (sender, e) =>
            {
                assemblies[e.LoadedAssembly.FullName] = e.LoadedAssembly;
            };
            AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
            {
                Assembly assembly = null;
                assemblies.TryGetValue(e.Name, out assembly);
                return assembly;
            };
        }


        public static Assembly CompileCode(string code, string className, string outputAssemblyPath = "")
        {
            //if (string.IsNullOrWhiteSpace(outputAssemblyPath))
            //    outputAssemblyPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "temp\\");

            var assembly = CSScriptLib.CSScript.Evaluator.CompileCode(code);//.RoslynEvaluator.CompileCode(code);
            //if (assembly != null)
            //    AppDomain.CurrentDomain.Load(assembly.GetName());
            return assembly;

            ////var provider = new CSharpCodeProvider();
            //var compiler = new CSharpCodeProvider();// provider.CreateCompiler();
            //var compilerparams = new CompilerParameters();
            //compilerparams.GenerateExecutable = false;
            //if (!string.IsNullOrWhiteSpace(outputAssemblyPath))
            //{
            //    compilerparams.GenerateInMemory = false;
            //    if (!System.IO.Directory.Exists(outputAssemblyPath))
            //        System.IO.Directory.CreateDirectory(outputAssemblyPath);

            //    compilerparams.OutputAssembly = System.IO.Path.Combine(outputAssemblyPath, className + ".dll");
            //}
            //else
            //{
            //    compilerparams.GenerateInMemory = true;
            //}
            //foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            //{
            //    try
            //    {
            //        string location = assembly.Location;
            //        if (!String.IsNullOrEmpty(location) && !compilerparams.ReferencedAssemblies.Contains(location))
            //        {
            //            compilerparams.ReferencedAssemblies.Add(location);
            //        }
            //    }
            //    catch (NotSupportedException)
            //    {
            //        // this happens for dynamic assemblies, so just ignore it. 
            //    }
            //}
            //if (OtherAssemblys?.Count > 0)
            //{
            //    OtherAssemblys.ForEach(f =>
            //    {
            //        if (!string.IsNullOrEmpty(f) && System.IO.File.Exists(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, f))
            //        && !compilerparams.ReferencedAssemblies.CastToList_<string>().Exists(ff => ff.ToLower().Contains(f.ToLower())))
            //            compilerparams.ReferencedAssemblies.Add(f);
            //    });
            //}
            //var results = compiler.CompileAssemblyFromSource(compilerparams, code);
            //if (results.Errors.HasErrors)
            //{
            //    StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
            //    foreach (CompilerError error in results.Errors)
            //    {
            //        errors.AppendFormat("Line {0},{1}\t: {2}\n",
            //               error.Line, error.Column, error.ErrorText);
            //    }
            //    throw new Exception(errors.ToString());
            //}
            //else
            //{
            //    if (!string.IsNullOrWhiteSpace(className)// && string.IsNullOrWhiteSpace(outputAssemblyPath)
            //        )
            //        AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
            //    return results.CompiledAssembly;
            //}
        }

        public static Assembly CompileCodeOrGetFromCache(string code, string key)
        {
            bool exists = cache.ContainsKey(key);
            if (!exists)
            {
                lock (syncRoot)
                {
                    exists = cache.ContainsKey(key);

                    if (!exists)
                    {
                        var assembly = CompileCode(code, key);
                        if (assembly != null)
                        {
                            cache.Add(key, assembly);
                           Log.Log.Debug($"RuntimeCodeCompiler.CompileCode And add to Cache.{ assembly.FullName} @{ key}");
                        }
                        else
                        {
                            return null;
                        }
                    }
                }
            }
            return cache[key];
        }
        public static Assembly CompileCodeOrGetFromCache(string key)
        {
            bool exists = cache.ContainsKey(key);
            if (exists)
            {
                return cache[key];
            }
            return null;
        }
        public static bool RemoveAllCache()
        {
            if (cache!=null)
            {
                var ret = true;
                cache.Clear();
                //WebApi.Utils.Log.Log.Debug($"RuntimeCodeCompiler.RemoveAllCache");
                return ret;
            }
            return false;
        }
        public static bool RemoveFromCache(string key)
        {
            bool exists = cache.ContainsKey(key);
            if (exists)
            {
                var ret= cache.Remove(key);
                //WebApi.Utils.Log.Log.Debug($"RuntimeCodeCompiler.RemoveFromCached.{ ret} @{ key}");
                return ret;
            }
            return false;
        }

    }
}